/* This file is part of GCC.

GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.

GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>. */

#include "config.h"
#include "system.h"
#include "ansidecl.h"
#include "coretypes.h"
#include "tm.h"
#include "opts.h"
#include "tree.h"
#include "tree-iterator.h"
#include "tree-pass.h"
#include "gimple.h"
#include "toplev.h"
#include "debug.h"
#include "options.h"
#include "flags.h"
#include "convert.h"
#include "diagnostic-core.h"
#include "langhooks.h"
#include "langhooks-def.h"
#include "target.h"
#include "cgraph.h"
#include "vec.h"

#include "gmp.h"
#include "mpfr.h"

#include "js-lang.h"
#include "js-tree.h"
#include "js-utils.h"

#define int3() asm volatile("int3");
/* Language-dependent contents of a type.  */
struct GTY(()) lang_type {
  char dummy;
} ;

/* Language-dependent contents of a decl.  */
struct GTY(()) lang_decl {
  char dummy;
} ;

/* Language-dependent contents of an identifier.  This must include a
   tree_identifier.
*/
struct GTY(()) lang_identifier {
  struct tree_identifier common;
} ;

/* The resulting tree type.  */
union GTY((desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"),
           chain_next ("(union lang_tree_node *) TREE_CHAIN (&%h.generic)")))
lang_tree_node
{
  union tree_node GTY((tag ("0"),
                       desc ("tree_node_structure (&%h)"))) generic;
  struct lang_identifier GTY((tag ("1"))) identifier;
};

/* We don't use language_function.  */
struct GTY(()) language_function {
    int dummy;
};

void __js_debug__( const char * file, unsigned int lineno,
                  const char *fmt, ... )
{
  va_list args;
  fprintf( stderr, "debug: <%s:%i> -> ",
          file, lineno );
  va_start( args, fmt );
  vfprintf( stderr, fmt, args );
  va_end( args );
  fprintf(stderr, "\n");
}

#define IS_DECL(expr) \
    ((TREE_CODE(expr) == VAR_DECL) || (TREE_CODE(expr) == PARM_DECL))

#define TODO() fprintf(stderr, "error at %s:%d\n", __FILE__, __LINE__);\
  error("TODO");exit(EXIT_FAILURE)
#define IS_ptr_type(node) (node == ptr_type_node)
#define IS_void_type(node) (node == void_type_node)
#define IS_string_type(node) (TREE_TYPE(node) && TREE_TYPE(node) == char_type_node)
#define IS_voidptr_type(node) (IS_ptr_type(node) && IS_void_type(TREE_TYPE(node)))

/* integer expr = (string) "abcd";
 * type1 = string, type2 = integer */
static tree __convert(tree type, tree expr) {
    tree old = expr;
    if (TREE_CODE(expr) == SAVE_EXPR) {
        expr = TREE_OPERAND(expr, 0);
    }
    if (TREE_CODE(expr) == VAR_DECL) {
        TREE_TYPE(expr) = type;
        TREE_TYPE(old)  = type;
        relayout_decl(expr);
    } else {
        expr = copy_node(expr);
        TREE_TYPE(expr) = type;
    }
    return old;
}

tree convert (tree type1, tree expr)
{
  tree type2 = TREE_TYPE(expr);
  if (TREE_TYPE(expr) == type1) {
      return expr;
  }
  if (type1 == integer_type_node) {
      return js_build_int_object(expr);
  }
  if (type2 == double_type_node) {
      return js_build_float_object(expr);
  }
  if (type1 == string_type_node) {
      expr = __convert(type1, expr);
  }
  else if (IS_voidptr_type(type2)) {
      expr = __convert(type1, expr);
  }
  return expr;
}

/* The attribute table might be used for the GCC attribute syntax, e.g.
 * __attribute__((unused)), but this feature isn't yet used in gcalc
 */ 
const struct attribute_spec js_attribute_table[] = {
      { NULL, 0, 0, false, false, false, NULL }
};

/* The language hooks data structure. This is the main interface between the GCC front-end
 * and the GCC middle-end/back-end. A list of language hooks could be found in
 * <gcc>/langhooks.h
 */
#undef LANG_HOOKS_NAME
#undef LANG_HOOKS_INIT 
#undef LANG_HOOKS_INIT_OPTIONS
#undef LANG_HOOKS_HANDLE_OPTION
#undef LANG_HOOKS_POST_OPTIONS
#undef LANG_HOOKS_PARSE_FILE
#undef LANG_HOOKS_TYPE_FOR_MODE
#undef LANG_HOOKS_TYPE_FOR_SIZE
#undef LANG_HOOKS_BUILTIN_FUNCTION
#undef LANG_HOOKS_GLOBAL_BINDINGS_P
#undef LANG_HOOKS_PUSHDECL
#undef LANG_HOOKS_GETDECLS
#undef LANG_HOOKS_WRITE_GLOBALS
#undef LANG_HOOKS_GIMPLIFY_EXPR

#define LANG_HOOKS_NAME                 "GNU JS"
#define LANG_HOOKS_INIT                 js_langhook_init
static bool js_langhook_init(void)
{
  build_common_tree_nodes(false);
  build_common_tree_nodes_2(0);
  js_init_tables();
  init_builtins();
  return true;
}
#undef LANG_HOOKS_FINISH
#define LANG_HOOKS_FINISH                 js_langhook_finish
static void js_langhook_finish(void)
{
  js_del_tables();
}

#define LANG_HOOKS_INIT_OPTIONS         js_langhook_init_options
static void js_langhook_init_options (unsigned int n ATTRIBUTE_UNUSED,
                                      struct cl_decoded_option * opts ATTRIBUTE_UNUSED)
{
  /*debug(__func__);*/
}

#define LANG_HOOKS_HANDLE_OPTION        js_langhook_handle_option
static bool js_langhook_handle_option (size_t code ATTRIBUTE_UNUSED,
                                       const char *arg ATTRIBUTE_UNUSED,
                                       int value ATTRIBUTE_UNUSED, int kind ATTRIBUTE_UNUSED,
                                       location_t loc ATTRIBUTE_UNUSED,
                                       const struct cl_option_handlers *handlers ATTRIBUTE_UNUSED)
{
  return false;
}
#define LANG_HOOKS_POST_OPTIONS         js_langhook_post_options
static bool js_langhook_post_options(const char ** ARG_UNUSED (pfilename))
{
  flag_excess_precision_cmdline = EXCESS_PRECISION_FAST;
  return false;
}

#define LANG_HOOKS_PARSE_FILE           js_langhook_parse_file
static void js_langhook_parse_file (void)
{
    debug(__func__);
#if 0
    fprintf(stderr, "num_in_fnames=%d\n", num_in_fnames);
#endif
    if (num_in_fnames) {
        debug0(in_fnames[0]);
        linemap_add(line_table, LC_ENTER, 0, in_fnames[0], 1);
        main_input_filename = in_fnames[0];
        js_parse_file(in_fnames[0]);
    }
}


#define LANG_HOOKS_TYPE_FOR_MODE        js_langhook_type_for_mode
static tree js_langhook_type_for_mode(enum machine_mode mode ATTRIBUTE_UNUSED,
                                      int unsignedp ATTRIBUTE_UNUSED)
{
  /*debug(__func__);*/
  return NULL_TREE;
}

#define LANG_HOOKS_TYPE_FOR_SIZE        js_langhook_type_for_size
static tree js_langhook_type_for_size(unsigned int bits ATTRIBUTE_UNUSED,
                                      int unsignedp ATTRIBUTE_UNUSED)
{
  debug(__func__);
  return NULL;
}

#define LANG_HOOKS_BUILTIN_FUNCTION     js_langhook_builtin_function
static tree js_langhook_builtin_function(tree decl ATTRIBUTE_UNUSED)
{
  debug(__func__);
  return decl;
}

#define LANG_HOOKS_GLOBAL_BINDINGS_P    js_langhook_global_bindings_p
static int js_langhook_global_bindings_p(tree decl ATTRIBUTE_UNUSED)
{
  debug(__func__);
  return 1;
}
#define LANG_HOOKS_PUSHDECL             js_langhook_pushdecl
static tree js_langhook_pushdecl(tree decl)
{
  debug(__func__);
  VEC_safe_push(tree, gc, global_decls, decl);
  return NULL;
}
#define LANG_HOOKS_GETDECLS             js_langhook_getdecls
static tree js_langhook_getdecls(void)
{
  debug(__func__);
  return NULL;
}
#define LANG_HOOKS_WRITE_GLOBALS        js_langhook_write_globals
static void js_langhook_write_globals(void)
{
  int i, j=0, len= VEC_length(tree,global_decls);
  tree itx = NULL_TREE;
  tree *vec = XNEWVEC( tree, len );
  debug(__func__);
  for(i=0; VEC_iterate(tree,global_decls,i,itx); ++i) {
      vec[j] = itx;
      if (TREE_CODE(itx) == IDENTIFIER_NODE) {
          const char *s = IDENTIFIER_POINTER(itx);
          fprintf(stderr, "global:%s\n", s);
      }
      if (TREE_CODE(itx) == VAR_DECL) {
          tree nm = DECL_NAME(itx);
          char *s = IDENTIFIER_POINTER(nm);
          fprintf(stderr, "global_var:%s\n", s);
      }
      j++;
  }

  wrapup_global_declarations(vec, len);
  check_global_declarations(vec, len);
  emit_debug_global_declarations(vec, len);
  cgraph_finalize_compilation_unit();
  free(vec);
}
#define LANG_HOOKS_GIMPLIFY_EXPR        js_langhook_gimplify_expr
static int js_langhook_gimplify_expr(tree *expr_p ATTRIBUTE_UNUSED,
                                     gimple_seq *pre_p ATTRIBUTE_UNUSED,
                                     gimple_seq *post_p ATTRIBUTE_UNUSED)
{
  return GS_UNHANDLED;
}

struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;

#include "gt-js-js-lang.h"
#include "gtype-js.h"
